/*  
*   This file is a part of Neutrino - a lightweight UI library accelerated with OpenGL
*   Copyright (C) 2023 bsolos
*
*   This program is free software: you can redistribute it and/or modify
*   it under the terms of the GNU General Public License as published by
*   the Free Software Foundation, either version 3 of the License, or
*   (at your option) any later version.
*
*   This program is distributed in the hope that it will be useful,
*   but WITHOUT ANY WARRANTY; without even the implied warranty of
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*   GNU General Public License for more details.
*
*   You should have received a copy of the GNU General Public License
*   along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <chrono>
#include <math.h>

#include <neutrino.hpp>

using namespace neutrino;


render_context::render_context() {
    std::cout << "trying to create render context" << std::endl;

    glfwInit();

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);

    glfw_window = glfwCreateWindow(800, 600, "Neutrino window", NULL, NULL);
    if (glfw_window == NULL) {
        throw std::runtime_error("glfwCreateWindow failed");
    }
    glfwMakeContextCurrent(glfw_window);

    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
        throw std::runtime_error("Failed to initialize GLAD");
    }    

    glfwSetWindowUserPointer(glfw_window, this);

    glViewport(0, 0, 800, 600);
    glfwSetFramebufferSizeCallback(glfw_window, framebuffer_size_callback);
    this->width = 800;
    this->height = 600;

    std::cout << "Created render context" << std::endl;
}


render_context::~render_context() {
    glfwTerminate();
    std::cout << "Terminated render context" << std::endl;
}


void render_context::main_loop() {
    while (!glfwWindowShouldClose(glfw_window)) {
        if (!clear_buffer) {
            render_incremental();
        } else {
            render_clear();
            clear_buffer = false;
        }
        
        if (do_present) {
            present();
            do_present = false;
        }

        glfwPollEvents();
    }
}


void render_context::mark_for_present() {
    do_present = true;
}


void render_context::render_incremental() {
    static double oxpos = 0, oypos = 0;
    double xpos, ypos;
    glfwGetCursorPos(glfw_window, &xpos, &ypos);
    if (oxpos != xpos || oypos != ypos) {
        window_surface s = create_surface();
        subsurface ss = s.create_subsurface((int)xpos, (int)ypos, 200, 200);

        ss.clear();

        ss.set_color({1.0, 1.0, 0.0, 1.0});
        point triangle_strip[] = {{50, 50}, {50, 150}, {150, 50}, {150, 150}};
        ss.draw_primitives(GL_TRIANGLE_STRIP, triangle_strip, 4);

        ss.set_color({1.0, 0.0, 0.0, 0.0});
        point line_strip[] = {{0, 0}, {200, 0}, {200, 200}, {0, 200}, {0, 0}};
        ss.draw_primitives(GL_LINE_STRIP, line_strip, 5);

        oxpos = xpos;
        oypos = ypos;
    }
}


void render_context::render_clear() {
    create_surface().clear();
    render_incremental();
    mark_for_present();
}


void render_context::present() {
    glfwSwapBuffers(glfw_window); 
    glReadBuffer(GL_FRONT);
    glDrawBuffer(GL_BACK);
    glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
}


void render_context::set_width(int width) {
    glfwSetWindowSize(glfw_window, width, height);
}


void render_context::set_height(int height) {
    glfwSetWindowSize(glfw_window, width, height);
}


void render_context::set_size(int width, int height) {
    glfwSetWindowSize(glfw_window, width, height);
}


window_surface render_context::create_surface() {
    return window_surface(this, width, height);
}


void render_context::framebuffer_size_callback(GLFWwindow* window, int width, int height) {
    render_context *ctx = (render_context*)glfwGetWindowUserPointer(window);
    ctx->on_framebuffer_size_changed(width, height);
}


void render_context::on_framebuffer_size_changed(int width, int height) {
    glViewport(0, 0, width, height);
    this->width = width;
    this->height = height;
    clear_buffer = true;
}


render_context *render_context::create() {
    return new render_context;
}


void render_context::destroy(render_context *ctx) {
    delete ctx;
}